Skip to content

ci(docker): allow a manual rebuild via workflow_dispatch - #129

Merged
MrChengLen merged 1 commit into
mainfrom
ci/docker-manual-trigger
Sep 14, 2026
Merged

MrChengLen merged 1 commit into
mainfrom
ci/docker-manual-trigger

Conversation

@MrChengLen

@MrChengLen MrChengLen commented Sep 14, 2026 •

Copy link
Copy Markdown
Owner

Warum

Docker lief bisher nur bei einem Push auf main oder einem Versions-Tag. main ist durch Pflicht-Checks geschützt — ein Image zu bauen (und damit notify-ops und den Deploy dahinter zu erreichen) setzte also immer einen Pull Request voraus, selbst wenn am Code nichts zu ändern war.

Genau diese Lücke war letzte Woche das Problem: Die Zugangsdaten, die notify-ops verwendet, waren am 20.08. abgelaufen; der Workflow scheiterte danach bei jedem Lauf mit HTTP 401, und drei Wochen lang erreichte kein Deploy die Produktion. Nach dem Ersetzen des Tokens gab es keine Möglichkeit, die neue Berechtigung zu prüfen oder den aktuellen main-Stand neu auszurollen, ohne eine Code-Änderung zu erfinden. Ein leerer Commit wird von den Branch-Regeln zurückgewiesen — korrekt so, und nichts, was man umgehen sollte.

workflow_dispatch baut aus dem, was gerade auf main liegt, und durchläuft die ganze Kette: bauen, pushen, signieren, benachrichtigen, deployen. Das ist auch das, was man im Störungsfall braucht, wo die Frage meist „den Stand von main neu ausrollen" lautet und nicht „eine Änderung ausliefern".

Was sich sonst ändert

Nichts. Build, Matrix, Signierung und Permissions bleiben unangetastet; die Supply-Chain-Guards des Repos greifen weiterhin (SHA-gepinnte Actions, expliziter permissions-Block) — tests/test_supply_chain_hygiene.py läuft grün durch.

Nach dem Merge

Der Merge selbst ist zugleich der erste Test des neuen Tokens: Er pusht auf main, also baut docker.yml und notify-ops.yml feuert. Ist der Lauf grün, ist die Kette nachweislich wieder intakt. Danach steht der Knopf unter Actions → Docker → Run workflow dauerhaft zur Verfügung.

🤖 Generated with Claude Code


Nachtrag: Die erste Fassung nannte das Secret beim Namen und wurde vom scope-check blockiert — korrekt, der Name gehört ausschließlich in notify-ops.yml, die dafür auf der Allowlist steht. Der Kommentar beschreibt den Vorfall jetzt ohne ihn.

Docker only ran on a push to main or a version tag. main is protected by
required status checks, so producing an image -- and reaching notify-ops and
the deploy behind it -- always meant opening a pull request, even when nothing
about the code needed to change.

That gap cost three weeks in September 2026: the credential notify-ops uses had
expired, it failed with HTTP 401 on every run, and no deploy reached production.
Once the credential was replaced there was no way to prove it worked, or to
redeploy the current main, without inventing a code change to push. An empty
commit is rejected by the branch rules, which is correct and not something to
work around.

workflow_dispatch rebuilds from whatever main currently holds and runs the whole
chain: build, push, sign, notify, deploy. That is also what is wanted during an
incident, where the question is usually "redeploy what is on main" rather than
"ship a change".

Nothing else moves. The build, the matrix, the signing and the permissions are
untouched, and the repo's supply-chain guards still pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@MrChengLen
MrChengLen force-pushed the ci/docker-manual-trigger branch from e6f0d46 to f72dedc Compare September 14, 2026 17:20
@MrChengLen
MrChengLen merged commit 8184715 into main Sep 14, 2026
5 checks passed
@MrChengLen
MrChengLen deleted the ci/docker-manual-trigger branch September 25, 2026 13:25
MrChengLen added a commit that referenced this pull request Sep 25, 2026
…ships

The image installs requirements.lock with --require-hashes, but four
workflows still installed requirements.txt, whose >= ranges resolve to
the newest PyPI releases. So CI tested, and the SBOM described, versions
the image does not ship.

lint-and-test: installs requirements-dev.txt with the lockfile's pins as
constraints. SQLAlchemy 2.1.0 reached CI unpinned and failed a test on
every branch while the image stayed on 2.0.52. The hashes are stripped
into $RUNNER_TEMP/constraints.txt because pip switches the whole install
to --require-hashes as soon as one constraint carries a hash (checked
with pip 26.2.1), and the dev tools are unhashed. The step fails if any
lockfile entry does not become a constraint, instead of letting it
install unpinned. The dev-only tools are not in the lockfile: a pip
dry-run for cp314/manylinux resolves them to the same pytest, ruff, uv,
aiosqlite, httpx, pip-audit, pyinstaller and bs4 versions as without
constraints, and 76 of the 77 locked packages at their locked version
(uvloop, Linux-only, was skipped on the Windows host that ran it).
A floor raised past the locked version (the usual Dependabot pip PR) now
fails this install as well as lockfile-drift; recompiling fixes both. A
dev tool that needs a newer locked package fails here with
lockfile-drift green; the ci.yml comment gives the --upgrade-package
recompile for that case.

deps-latest (new): the unpinned install plus pytest, weekly on Mondays
and on demand, gating nothing. It keeps the early warning that caught
2.1 before any lockfile bump. Not a continue-on-error job in ci.yml:
that still shows a red check on every PR whenever upstream breaks.

sbom / release: the SBOM described the runner's own Python after
`pip install -r requirements.txt` plus cyclonedx-bom. main's SBOM at
1d7bad6 had 106 components against 77 in the lockfile: 19 at versions
the image does not contain, 28 from the generator itself, and packaging
downgraded from 26.3 to 25.0 by the generator's install. Now: a fresh
venv installed the Dockerfile's way, the generator outside it, and
`cyclonedx-py environment <venv python>` (checked with cyclonedx-bom
5.5.0). Rejected `cyclonedx-py requirements requirements.lock`: it
parses the hashed lockfile fine but emits no licences and no dependency
graph (main's SBOM carries 70 licensed components, 61 graph entries).
The docs now say which workflow builds the release SBOM (release.yml,
not sbom.yml) and that system packages are not in it.

verapdf: built its PDF/A fixture with the newest pikepdf; it now
installs the lockfile the image's way (all 77 locked versions have
cp314 manylinux wheels). build-desktop.yml still installs
requirements.txt: it builds on Windows, the lockfile is Linux-only.

docker.yml: renormalized to LF. PR #129 committed it with CRLF via an
API commit despite `*.yml text eol=lf`, so every checkout showed it as
modified. No content change.

tests/test_supply_chain_hygiene.py pins all of it, including that
deps-latest keeps lint-and-test's system packages and pytest call; each
of 10 simulated reverts fails a guard.

Local suite 1225 passed, 63 skipped (native-library tests run in CI);
ruff clean; python-version gate green; i18n and pip-audit inputs
untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MrChengLen added a commit that referenced this pull request Sep 25, 2026
…ships

The image installs requirements.lock with --require-hashes, but four
workflows still installed requirements.txt, whose >= ranges resolve to
the newest PyPI releases. So CI tested, and the SBOM described, versions
the image does not ship.

lint-and-test: installs requirements-dev.txt with the lockfile's pins as
constraints. SQLAlchemy 2.1.0 reached CI unpinned and failed a test on
every branch while the image stayed on 2.0.52. The hashes are stripped
into $RUNNER_TEMP/constraints.txt because pip switches the whole install
to --require-hashes as soon as one constraint carries a hash (checked
with pip 26.2.1), and the dev tools are unhashed. The step fails if any
lockfile entry does not become a constraint, instead of letting it
install unpinned. The dev-only tools are not in the lockfile: a pip
dry-run for cp314/manylinux resolves them to the same pytest, ruff, uv,
aiosqlite, httpx, pip-audit, pyinstaller and bs4 versions as without
constraints, and 76 of the 77 locked packages at their locked version
(uvloop, Linux-only, was skipped on the Windows host that ran it).
A floor raised past the locked version (the usual Dependabot pip PR) now
fails this install as well as lockfile-drift; recompiling fixes both. A
dev tool that needs a newer locked package fails here with
lockfile-drift green; the ci.yml comment gives the --upgrade-package
recompile for that case.

deps-latest (new): the unpinned install plus pytest, weekly on Mondays
and on demand, gating nothing. It keeps the early warning that caught
2.1 before any lockfile bump. Not a continue-on-error job in ci.yml:
that still shows a red check on every PR whenever upstream breaks.

sbom / release: the SBOM described the runner's own Python after
`pip install -r requirements.txt` plus cyclonedx-bom. main's SBOM at
1d7bad6 had 106 components against 77 in the lockfile: 19 at versions
the image does not contain, 28 from the generator itself, and packaging
downgraded from 26.3 to 25.0 by the generator's install. Now: a fresh
venv installed the Dockerfile's way, the generator outside it, and
`cyclonedx-py environment <venv python>` (checked with cyclonedx-bom
5.5.0). Rejected `cyclonedx-py requirements requirements.lock`: it
parses the hashed lockfile fine but emits no licences and no dependency
graph (main's SBOM carries 70 licensed components, 61 graph entries).
The docs now say which workflow builds the release SBOM (release.yml,
not sbom.yml) and that system packages are not in it.

verapdf: built its PDF/A fixture with the newest pikepdf; it now
installs the lockfile the image's way (all 77 locked versions have
cp314 manylinux wheels). build-desktop.yml still installs
requirements.txt: it builds on Windows, the lockfile is Linux-only.

docker.yml: renormalized to LF. PR #129 committed it with CRLF via an
API commit despite `*.yml text eol=lf`, so every checkout showed it as
modified. No content change.

tests/test_supply_chain_hygiene.py pins all of it, including that
deps-latest keeps lint-and-test's system packages and pytest call; each
of 10 simulated reverts fails a guard.

Local suite 1239 passed, 63 skipped (native-library tests run in CI);
ruff clean; python-version gate green; i18n and pip-audit inputs
untouched.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant